package scu.maqiang.mesh;

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

import scu.maqiang.numeric.CG;
import scu.maqiang.numeric.MVO;

/**
 * 三维四面体网格类
 */
public class Mesh3T4 extends Mesh{
	public Mesh3T4() {
		super();
		nDim = 3;
		nPerElement = 4;
		nPerBoundary = 3;
		nEdgePerElement = 6;
		nEdgePerBoundary = 3;
		nBoundaryPerElement = 4;
		tecplotType = "FETETRAHEDRON";
		meditMeshType = new String[]{"Vertices", "Tetrahedra", "Triangles"};
		gmshType = new String[]{"ST", "VT", "TT"};
		gmshElementType = 4;
	}

	public Mesh3T4 cube(int xNum, int yNum, int zNum) {
		int xNodes = xNum + 1;
		int yNodes = yNum + 1;
		int zNodes = zNum + 1;
		nv = xNodes * yNodes * zNodes;
		nt = 6 * xNum * yNum * zNum;
		nb = 4 * (xNum * yNum + yNum * zNum + xNum * zNum);
		initMesh0(nv, nt, nb);
		int count = 0;
		for (int k = 0; k < zNodes; k++) {
			for (int j = 0; j < yNodes; j++) {
				for (int i = 0; i < xNodes; i++) {
					nodes[count++] = new double[]{(i + 0.0) / xNum, (j + 0.0) / yNum, (k + 0.0) / zNum};
				}
			}
		}

		// �洢�����嵥Ԫ���
		count = 0;
		int L1, L2, L3, L4, L5, L6, L7, L8;
		for (int k = 0; k < zNum; k++) {
			for (int j = 0; j < yNum; j++) {
				for (int i = 0; i < xNum; i++) {
					L1 = i + j * xNodes + k * xNodes * yNodes;
					L2 = L1 + 1;
					L3 = L2 + xNodes;
					L4 = L3 - 1;
					int temp = xNodes * yNodes;
					L5 = L1 + temp;
					L6 = L2 + temp;
					L7 = L3 + temp;
					L8 = L4 + temp;
					elements[count++] = new int[]{L1, L2, L3, L7};
					elements[count++] = new int[]{L1, L6, L2, L7};
					elements[count++] = new int[]{L1, L3, L4, L7};
					elements[count++] = new int[]{L1, L5, L6, L7};
					elements[count++] = new int[]{L1, L4, L8, L7};
					elements[count++] = new int[]{L1, L8, L5, L7};
				}
			}
		}

		//z = 0平面边上单元设为1
		count = 0;
		for (int j = 0; j < yNum; j++) {
			for (int i = 0; i < xNum; i++) {
				L1 = i + j * xNodes;
				L2 = L1 + 1;
				L3 = L2 + xNodes;
				L4 = L3 - 1;
				boundaries[count] = new int[]{L1, L3, L2};
				boundaryLabel[count++] = 1;
				boundaries[count] = new int[]{L1, L4, L3};
				boundaryLabel[count++] = 1;
			}
		}

		//z = 1平面边界编号设为2
		for (int j = 0; j < yNum; j++) {
			for (int i = 0; i < xNum; i++) {
				int temp = zNum * xNodes * yNodes;
				L1 = i + j * xNodes + temp;
				L2 = L1 + 1;
				L3 = L2 + xNodes;
				L4 = L3 - 1;
				boundaries[count] = new int[]{L1, L3, L2};
				boundaryLabel[count++] = 2;
				boundaries[count] = new int[]{L1, L4, L3};
				boundaryLabel[count++] = 2;
			}
		}

		//x = 0平面边界设为3
		for (int j = 0; j < zNum; j++) {
			for (int i = 0; i < yNum; i++) {
				L1 = i * xNodes + j * xNodes * yNodes;
				L2 = L1 + xNodes;
				L3 = L2 + xNodes * yNodes;
				L4 = L1 + xNodes * yNodes;
				boundaries[count] = new int[]{L1, L2, L3};
				boundaryLabel[count++] = 3;
				boundaries[count] = new int[]{L1, L3, L4};
				boundaryLabel[count++] = 3;
			}
		}

		// x = 1平面边界设为4
		for (int j = 0; j < zNum; j++) {
			for (int i = 0; i < yNum; i++) {
				L1 = xNum + i * xNodes + j * xNodes * yNodes;
				L2 = L1 + xNodes;
				L3 = L2 + xNodes * yNodes;
				L4 = L1 + xNodes * yNodes;
				boundaries[count] = new int[]{L1, L2, L3};
				boundaryLabel[count++] = 4;
				boundaries[count] = new int[]{L1, L3, L4};
				boundaryLabel[count++] = 4;
			}
		}

		// y = 0平面边界设为5
		for (int j = 0; j < zNum; j++) {
			for (int i = 0; i < xNum; i++) {
				L1 = i + j * xNodes * yNodes;
				L2 = L1 + 1;
				L3 = L2 + xNodes * yNodes;
				L4 = L3 - 1;
				boundaries[count] = new int[]{L1, L2, L3};
				boundaryLabel[count++] = 5;
				boundaries[count] = new int[]{L1, L3, L4};
				boundaryLabel[count++] = 5;
			}
		}

		// y = 1平面边界设为6
		for (int j = 0; j < zNum; j++) {
			for (int i = 0; i < xNum; i++) {
				int temp = yNum * xNodes;
				L1 = i + j * xNodes * yNodes + temp;
				L2 = L1 + 1;
				L3 = L2 + xNodes * yNodes;
				L4 = L3 - 1;
				boundaries[count] = new int[]{L1, L3, L2};
				boundaryLabel[count++] = 6;
				boundaries[count] = new int[]{L1, L4, L3};
				boundaryLabel[count++] = 6;
			}
		}
		return this;
	}


	/**
	 * 以".mesh" 格式保存网格文件
	 * @param fileName 网格文件名
	 */
	public void saveMesh_mesh(String fileName) {
		try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {
			bw.write("MeshVersionFormatted 1\n\n");
			bw.write("Dimension 3\n\n");
			bw.write("Vertices\n");
			bw.write(nv +"\n");
			for (int i = 0; i < nv; i++) {
				double[] node = nodes[i];
				for (int j = 0, n = node.length; j < n; j++) {
					bw.write(node[j] + "\t ");
				}
				bw.write(nodeLabel[i] + "\n");
			}
			bw.write("\nTetrahedra\n");
			bw.write(nt + "\n");
			for (int i = 0; i < nt; i++) {
				int[] ele = elements[i];
				for (int j = 0, n = ele.length; j < n; j++) {
					bw.write((ele[j] + 1) + "\t ");
				}
				bw.write(elementLabel[i] + "\n");
			}
			bw.write("\nTriangles\n");
			bw.write(nb + "\n");
			for (int i = 0; i < nb; i++) {
				int[] ele = boundaries[i];
				for (int j = 0, n = ele.length; j < n; j++) {
					bw.write((ele[j] + 1) + "\t ");
				}
				bw.write(boundaryLabel[i] + "\n");
			}
			bw.write("\nEnd\n");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Mesh3T4 readMesh(String fileName, MeshFormat mf) {
		String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
		System.out.println("Reading \'." + prefix + "\' File...");
		switch (mf) {
			case mesh:
				readMeshFile(fileName);
				break;
			case msh:
				readMshFile(fileName);
				break;
			case msh1:
				readMshFileOld(fileName);
				break;
			case matMsh:
				readMatMshFile(fileName);
			default:
				break;
		}
		System.out.println("Finish Reading!");
		return this;
	}

	private void readMeshFile(String fileName) {
		try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
			String ll = br.readLine();
			while (!"Vertices".equals(ll.trim())) {
				ll = br.readLine();
			}
			nv = Integer.parseInt(br.readLine());
			nodes = new double[nv][];
			nodeLabel = new int[nv];
			for (int i = 0; i < nv; i++) {
				String[] data = br.readLine().split("\\s+");
				double x = Double.parseDouble(data[0]);
				double y = Double.parseDouble(data[1]);
				double z = Double.parseDouble(data[2]);
				int num = Integer.parseInt(data[3]);
				nodes[i] = new double[]{x, y, z};
				nodeLabel[i] = num;
			}
			while (!"Tetrahedra".equals(ll.trim())) {
				ll = br.readLine();
			}
			nt = Integer.parseInt(br.readLine());
			elements = new int[nt][];
			elementLabel = new int[nt];
			for (int i = 0; i < nt; i++) {
				String[] data = br.readLine().split("\\s+");
				int ti = Integer.parseInt(data[0]) - 1;
				int tj = Integer.parseInt(data[1]) - 1;
				int tk = Integer.parseInt(data[2]) - 1;
				int tl = Integer.parseInt(data[3]) - 1;
				int num = Integer.parseInt(data[4]);
				elements[i] = new int[]{ti, tj, tk, tl};
				elementLabel[i] = num;
			}
			while (!"Triangles".equals(ll)) {
				ll = br.readLine();
			}
			nb = Integer.parseInt(br.readLine());
			nb = nb;
			boundaries = new int[nb][];
			boundaryLabel = new int[nb];
			for (int i = 0; i < nb; i++) {
				String[] data = br.readLine().split("\\s+");
				int ti = Integer.parseInt(data[0]) - 1;
				int tj = Integer.parseInt(data[1]) - 1;
				int tk = Integer.parseInt(data[2]) - 1;
				int num = Integer.parseInt(data[3]);
				boundaries[i] = new int[]{ti, tj, tk};
				boundaryLabel[i] = num;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void readMshFile(String fileName) {
		Scanner sc = null;
		int nv, nt, nb;
		try {
			sc = new Scanner(new File(fileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		nv = sc.nextInt();
		nt = sc.nextInt();
		nb = sc.nextInt();
		initMesh0(nv, nt, nb);

		for (int i = 0; i < nv; i++) {
			double x = sc.nextDouble();
			double y = sc.nextDouble();
			double z = sc.nextDouble();
			nodes[i] = new double[]{x, y, z};
			nodeLabel[i] = sc.nextInt();
		}

		for (int i = 0; i < nt; i++) {
			int ti = sc.nextInt() - 1;
			int tj = sc.nextInt() - 1;
			int tk = sc.nextInt() - 1;
			int tl = sc.nextInt() - 1;
			int num = sc.nextInt();
			elements[i] = new int[]{ti, tj, tk, tl};
			elementLabel[i] = num;
		}

		for (int i = 0; i < nb; i++) {
			int bi = sc.nextInt() - 1;
			int bj = sc.nextInt() - 1;
			int bk = sc.nextInt() - 1;
			int num = sc.nextInt();
			boundaries[i] = new int[]{bi, bj, bk};
			boundaryLabel[i] = num;
		}

		sc.close();
	}

	public void readMshFileOld(String fileName) {
		Scanner sc = null;
		try {
			sc = new Scanner(new File(fileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		nv = sc.nextInt();
		nt = sc.nextInt();
		nb = sc.nextInt();
		initMesh0(nv, nt, nb);

		for (int i = 0; i < nv; i++) {
			double x = sc.nextDouble();
			double y = sc.nextDouble();
			double z = sc.nextDouble();
			nodes[i] = new double[]{x, y, z};
		}

		for (int i = 0; i < nt; i++) {
			int ti = sc.nextInt() - 1;
			int tj = sc.nextInt() - 1;
			int tk = sc.nextInt() - 1;
			int tl = sc.nextInt() - 1;
			int num = sc.nextInt();
			elements[i] = new int[]{ti, tj, tk, tl};
			elementLabel[i] = num;
		}

		for (int i = 0; i < nb; i++) {
			int bi = sc.nextInt() - 1;
			int bj = sc.nextInt() - 1;
			int bk = sc.nextInt() - 1;
			int num = sc.nextInt();
			boundaries[i] = new int[]{bi, bj, bk};
			boundaryLabel[i] = num;
		}

		sc.close();
	}

	private void readMatMshFile(String fileName) {
		Scanner sc = null;
		try {
			sc = new Scanner(new File(fileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		nv = sc.nextInt();
		nt = sc.nextInt();
		nodes = new double[nv][];
		nodeLabel = new int[nv];
		elements = new int[nt][];
		elementLabel = new int[nt];

		for (int i = 0; i < nv; i++) {
			double x = sc.nextDouble();
			double y = sc.nextDouble();
			double z = sc.nextDouble();
			nodes[i] = new double[]{x, y, z};
			nodeLabel[i] = sc.nextInt();
		}

		for (int i = 0; i < nt; i++) {
			int ti = sc.nextInt() - 1;
			int tj = sc.nextInt() - 1;
			int tk = sc.nextInt() - 1;
			int tl = sc.nextInt() - 1;
			int num = sc.nextInt();
			elements[i] = new int[]{ti, tj, tk, tl};
			elementLabel[i] = num;
		}
		sc.close();
		extractBoundarysFromElements();
	}

	public Mesh3T4 readMesh(String xyzFile, String eleFile) {
		Scanner scXYZ = null;
		Scanner scEle = null;
		FileWriter fwOther = null;
		String line = "";
		try {
			scXYZ = new Scanner(new File(xyzFile));
			scEle = new Scanner(new File(eleFile));
		} catch (IOException e) {
			e.printStackTrace();
		}

		ArrayList<double[]> nodes = new ArrayList<>();
		while(scXYZ.hasNext()) {
			line = scXYZ.nextLine();
			String[] xy = line.split("\\s+");
			double t1 = Double.parseDouble(xy[0]);
			double t2 = Double.parseDouble(xy[1]);
			double t3 = Double.parseDouble(xy[2]);
			//System.out.println(t1 + "\t" + t2 + "\t" + t3);
			nodes.add(new double[] {t1, t2, t3});
		}
		ArrayList<int[]> eles = new ArrayList<>();
		while(scEle.hasNext()) {
			line = scEle.nextLine();
			String[] ele = line.split("\\s+");
			int t1 = (int)(Double.parseDouble(ele[0]));
			int t2 = (int)(Double.parseDouble(ele[1]));
			int t3 = (int)(Double.parseDouble(ele[2]));
			int t4 = (int)(Double.parseDouble(ele[3]));
			eles.add(new int[] {t1, t2, t3, t4});
		}
		nv = nodes.size();
		nt = eles.size();
		nb = 0;
		initMesh0(nv, nt, nb);
		for(int i = 0; i < nv; i++) {
			this.nodes[i] = nodes.get(i);
		}
		for(int i = 0; i < nt; i++) {
			elements[i] = eles.get(i);
		}
		extractBoundarysFromElements();

		return this;
	}

	public Mesh3T4 readMeshVTU(String xyzFile, String eleFile) {
		Scanner scXYZ = null;
		Scanner scEle = null;
		FileWriter fwOther = null;
		String line = "";
		try {
			scXYZ = new Scanner(new File(xyzFile));
			scEle = new Scanner(new File(eleFile));
		} catch (IOException e) {
			e.printStackTrace();
		}

		ArrayList<double[]> nodes = new ArrayList<>();
		while(scXYZ.hasNext()) {
			double t1 = scXYZ.nextDouble();
			double t2 = scXYZ.nextDouble();
			double t3 = scXYZ.nextDouble();
			//System.out.println(t1 + "\t" + t2 + "\t" + t3);
			nodes.add(new double[] {t1, t2, t3});
		}
		ArrayList<int[]> eles = new ArrayList<>();
		while(scEle.hasNext()) {
			int t1 = scEle.nextInt();
			int t2 = scEle.nextInt();
			int t3 = scEle.nextInt();
			int t4 = scEle.nextInt();
			eles.add(new int[] {t1, t2, t3, t4});
		}
		nv = nodes.size();
		nt = eles.size();
		nb = 0;
		initMesh0(nv, nt, nb);
		for(int i = 0; i < nv; i++) {
			this.nodes[i] = nodes.get(i);
		}
		for(int i = 0; i < nt; i++) {
			elements[i] = eles.get(i);
		}
		extractBoundarysFromElements();

		return this;
	}


	@Override
	public double[] getinteriorPointInElement(double[][] coord) {
		double[] x = new double[4];
		double[] y = new double[4];
		double[] z = new double[4];
		for (int i = 0; i < 4; i++) {
			x[i] = coord[i][0];
			y[i] = coord[i][1];
			z[i] = coord[i][2];
		}
		return new double[]{ MVO.average(x), MVO.average(y), MVO.average(z)};
	}

	@Override
	public double[] getinteriorPointOnBoundary(double[][] coord) {
		double[] x = new double[3];
		double[] y = new double[3];
		double[] z = new double[3];
		for (int i = 0; i < 3; i++) {
			x[i] = coord[i][0];
			y[i] = coord[i][1];
			z[i] = coord[i][2];
		}
		return new double[]{ MVO.average(x), MVO.average(y), MVO.average(z)};
	}
	
	@Override
	public boolean checkMesh(double[][] coord) {
		return CG.volume3DT4(coord) > 0;
	}

	@Override
	public int[][] getBoundarysFromElement(int i) {
		int[][] bd = new int[nBoundaryPerElement][];
		int[] ele = elements[i];
		bd[0] = new int[] {ele[0], ele[2], ele[1]};
		bd[1] = new int[] {ele[0], ele[1], ele[3]};
		bd[2] = new int[] {ele[1], ele[2], ele[3]};
		bd[3] = new int[] {ele[2], ele[0], ele[3]};
		return bd;
	}

	@Override
	public int[][] getEdgesFromElement(int i) {
		int[][] result = new int[6][];
		int[] ele = elements[i];
		result[0] = new int[] {ele[0], ele[1]};
		result[1] = new int[] {ele[1], ele[2]};
		result[2] = new int[] {ele[2], ele[0]};
		result[3] = new int[] {ele[0], ele[3]};
		result[4] = new int[] {ele[1], ele[3]};
		result[5] = new int[] {ele[2], ele[3]};
		return result;
	}

	public int[][] getEdgesFromBoundary(int i) {
		int[] ele = boundaries[i];
		int[][] result = {{ele[0], ele[1]}, 
				          {ele[1], ele[2]}, 
				          {ele[2], ele[0]}};
		return result;
	}
	
	@Override
	public int getTecplotNt() {
		return nt;
	}
}
